﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MacomberMapSystem.Common.Internals
{
    /// <summary>
    /// This class offers a size-constrained undo/redo stack
    /// </summary>
    /// <typeparam name="T">The type to be contained within this stack</typeparam>
    /// <remarks>Loosely based on http://www.codeproject.com/KB/cs/generic_undo_redo.aspx</remarks>
    public class MM_UndoRedoStack<T>
    {

        #region Variable declarations
        /// <summary>Our list of items wihtin the stack</summary>
        private T[] StackItems;

        /// <summary>Our top point</summary>
        private int Top = 1;

        /// <summary>Our bottom point</summary>
        private int Bottom = 0;
        #endregion

        #region Stack properties
        /// <summary>
        /// Retrieve the number of items in the stack
        /// </summary>
        public int Count
        {
            get
            {
                int Tally = Top - Bottom - 1;
                if (Tally < 0)
                    Tally += StackItems.Length;
                return Tally;
            }
        }

        /// <summary>
        /// Retrieve the capacity of the stack
        /// </summary>
        public int Capacity
        {
            get
            {
                return StackItems.Length - 1;
            }
        }
        #endregion


        #region Initialization
        /// <summary>
        /// Create a new Undo/Redo stack of set capacity
        /// </summary>
        /// <param name="Capacity">The capacity to be used</param>
        public MM_UndoRedoStack(int Capacity)
        {
            StackItems = new T[Capacity + 1];
        }
        #endregion


        #region Stack manipulation
        /// <summary>
        /// Pop an element from the top of the stack
        /// </summary>
        /// <returns></returns>
        public T Pop()
        {
            T RemovedItem = StackItems[Top];
            StackItems[Top--] = default(T);
            if (Top < 0)
                Top += StackItems.Length;
            return RemovedItem;
        }

        /// <summary>
        /// Push an item onto our stack
        /// </summary>
        /// <param name="Item">The item to be added</param>
        /// <returns></returns>
        public void Push(T Item)
        {
            //If we're full, replace the last item, and adjust accordingly. Otherwise, add it to the stack
            if ((Top == Bottom) && (++Bottom >= StackItems.Length))
                Bottom -= StackItems.Length;
            if (++Top >= StackItems.Length)
                Top -= StackItems.Length;
            StackItems[Top] = Item;
        }

        /// <summary>
        /// Peek for an item on the stack
        /// </summary>
        /// <returns></returns>
        public T Peek()
        {
            return StackItems[Top];
        }

        /// <summary>
        /// Clear our stack
        /// </summary>
        public void Clear()
        {
            for (int a = 0; a < StackItems.Length; a++)
                StackItems[a] = default(T);
            Top = 1;
            Bottom = 0;
        }
        #endregion
    }
}
